import os
class ROM:
    ROM_FILE_MIME = [".GBA", ".GBC", ".GB"]
    BIOS_FILE_MIME = [".BIOS", ".BIN"]
    FLASHTYPE = "FLASH_512MV"
    UINT8BYTE = 1
    UINT16BYTE = 2
    UINT32BYTE = 4
    
    def __init__(self, dir):
        self.rootdir = dir
        self.files = []
        self.caches = {}
        self.__loadLocalFiles__(dir, self.files)
        
    def __loadLocalFiles__(self, rootdir, lst):
        index = 1
        if(lst):index += lst[-1]["index"]
        for root, dirs, files in os.walk(rootdir):
            for name in files:
                mime = os.path.splitext(name)[-1]
                if(mime.upper() in ROM.BIOS_FILE_MIME):
                    lst.append({"title":name,"path":os.path.join(root,name),"id":0})
                elif(mime.upper() in ROM.ROM_FILE_MIME):
                    lst.append({"title":name,"path":os.path.join(root,name),"id":index})
                    index += 1
            lst.sort(key=lambda x:x["id"])
            for dir in dirs:
                self.__loadLocalFiles__(os.path.join(root,dir), lst)
    
    def getNames(self):
        return {file["id"]:file["title"] for file in self.files if file["id"] != 0}
    
    def getInfo(self, id):
        if(id not in self.caches.keys()):self.__loadData__(id)
        rom = self.caches[id]
        is128 = rom["type"] >= 4
        isAtmel = rom["type"] <= 1
        return {"name": rom["name"], "length":len(rom["data"]), "is128":is128, "isAtmel":isAtmel}
        
    
    def __loadData__(self, id):
        self.caches[id] = {}
        path = [file["path"] for file in self.files if file["id"] == id][0]
        with open(path, 'rb') as f:
            bytearray = f.read()
            self.caches[id]["data"] = self.__loadBytes__(bytearray, ROM.UINT8BYTE)
            # self.caches[id][ROM.UINT16] = self.__loadBytes__(bytearray, ROM.UINT16BYTE)
            # self.caches[id][ROM.UINT32] = self.__loadBytes__(bytearray, ROM.UINT32BYTE)
        self.caches[id]["name"] = self.__getName__(id)
        self.caches[id]["type"] = self.__getType__(id)
            
            
    def __loadBytes__(self, bytes, offset):
        array = []
        for index in range(0, len(bytes), offset):
            uint = 0
            bit = 0
            for byte in bytes[index : index + offset]:
                uint |= byte << bit
                bit += 8
            array.append(uint)
        return array
    
    def __getName__(self, id):
        length = len(self.caches[id]["data"])
        name = ""
        if(length >= 0xC0):
            for address in range(0xA0, 0xB2, 1):
                if(self.caches[id]["data"][address] > 0): name += chr(self.caches[id]["data"][address])
        return name
    
    def __getType__(self, id):
        flashTypes = 0
        flash512mv = [ord(char) & 0xFF for char in ROM.FLASHTYPE]
        length = len(self.caches[id]["data"]) - 12
        for index in range(0, length, 4):
            flasSet = [self.caches[id]["data"][index | byte] for byte in [0, 1, 2, 3]]
            if(flasSet == flash512mv[:ROM.FLASHTYPE.index("H")]):
                offset = index + 4
                hv_512Set = [self.caches[id]["data"][offset | byte] for byte in [0, 1, 2, 3]]
                if(hv_512Set[0] == flash512mv[ROM.FLASHTYPE.index("H")]):
                    if(hv_512Set[1] == flash512mv[ROM.FLASHTYPE.index("_")]):
                        if(hv_512Set[2] == flash512mv[ROM.FLASHTYPE.index("V")]):flashTypes |= 1
                    elif(hv_512Set[1] == flash512mv[ROM.FLASHTYPE.index("5")]):
                        if(hv_512Set[2] == flash512mv[ROM.FLASHTYPE.index("1")] and hv_512Set[3] == flash512mv[ROM.FLASHTYPE.index("2")]):
                            offset += 4
                            v_Set = [self.caches[id]["data"][offset | byte] for byte in [0, 1]]
                            if(v_Set[0] == flash512mv[ROM.FLASHTYPE.index("_")] and v_Set[1] == flash512mv[ROM.FLASHTYPE.index("V")]):flashTypes |= 2
                elif(hv_512Set[1] == flash512mv[ROM.FLASHTYPE.index("1")] and hv_512Set[2] == flash512mv[ROM.FLASHTYPE.index("M")] and hv_512Set[3] == flash512mv[ROM.FLASHTYPE.index("_")]):
                    offset += 4
                    if(self.caches[id]["data"][offset] == flash512mv[ROM.FLASHTYPE.index("V")]):
                        flashTypes |= 4
                        break
        return flashTypes
    
    def getData(self, id, address, length):
        if(id not in self.caches.keys()):self.__loadData__(id)
        length = min(address + length, len(self.caches[id]["data"]))
        return bytearray(self.caches[id]["data"][address:length])